package it.tukano.treditor.modules;

import it.tukano.app.AbstractAppModule;
import it.tukano.app.AppModule;
import it.tukano.check.Todo;
import it.tukano.collections.HashMapExt;
import it.tukano.collections.MapExt;
import it.tukano.fun.Function1;
import it.tukano.fun.VFunction1;
import it.tukano.reflection.MethodExt;
import it.tukano.swingdeco.containers.JFrameExt;
import it.tukano.swingdeco.containers.RegionPanel;
import it.tukano.swingdeco.joptionpane.JOptionPaneExt;
import it.tukano.swingdeco.menubar.JMenuBarExt;
import it.tukano.treditor.resources.Labels;
import it.tukano.treditor.resources.Labels.LabelKey;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JOptionPane;

/**
 * Application's main frame
 * @author pgi
 */
public class MainFrame extends AbstractAppModule {
    
    /* Handles references to components added to the REGIONS container to check if the
     component can be dragged or not. The engine display cannot be dragged between tabs */
    private final MapExt<Component, Boolean> DRAGGABLE_COMPONENTS = new HashMapExt<>();
    
    /* A container splitted in five regions where each region contains a tabbed pane */
    private final RegionPanel REGIONS = new RegionPanel()
            .enableTabsDragAndDrop(new Function1<Component, Boolean>() {

                public Boolean apply(Component p) {
                    return DRAGGABLE_COMPONENTS.getOrElse(p, Boolean.FALSE);
                }
            });
    
    /* the main menu bar */
    private final JMenuBarExt menuBar = new JMenuBarExt();
    
    private final Dimension DEFAULT_FRAME_SIZE = new Dimension(800, 600);
    
    /* The main frame */
    private final JFrameExt FRAME = new JFrameExt(Labels.getInstance().getLabelValue(LabelKey.MAIN_FRAME_TITLE))
            .withMenuBar(menuBar)
            .append(REGIONS, BorderLayout.CENTER)
            .doNothingOnClose()
            .withSize(DEFAULT_FRAME_SIZE.width, DEFAULT_FRAME_SIZE.height)
            .onClosing(new MethodExt(this, "onFrameClose"))
            .onClosed(new MethodExt(this, "startJvmKillerThread"));

    /**
     * Installs a component into the region container held by this frame
     * @param component the component to install
     * @param borderLayoutLocation where to add the component (BorderLayout.[NORTH | SOUTH | EAST | WEST | CENTER])
     * @param draggable true if the component can be dragged from tab to tab, false otherwise
     */
    public void install(JComponent component, String borderLayoutLocation, boolean draggable) {
        DRAGGABLE_COMPONENTS.put(component, draggable);
        REGIONS.display(component, borderLayoutLocation);
    }
    
    /**
     * Remove a component added to the regions container with
     * the install method
     * @param component the component to remove
     */
    public void uninstall(JComponent component) {
        DRAGGABLE_COMPONENTS.remove(component);
        REGIONS.hide(component);
    }

    /** Show the main frame on screen */
    @Override
    public void startModule(AppModule context) {
        withModule(AppSettingsModule.class, new VFunction1<AppSettingsModule>() {

            @Override
            protected void doApply(AppSettingsModule p) {
                Dimension frameSize = p.getSettingOrElse(Dimension.class, "main frame size", DEFAULT_FRAME_SIZE);
                FRAME.setSize(frameSize);
            }
        });
        FRAME.setVisible(true);
    }

    /** Dispose the main frame */
    @Override
    public void destroyModule(AppModule context) {
        FRAME.dispose();
    }
    
    /** Invoked when the user closes the main frame, shutdown the application */
    public void onFrameClose() {
        withModule(AppSettingsModule.class, new VFunction1<AppSettingsModule>() {

            @Override
            protected void doApply(AppSettingsModule p) {
                Dimension storedSize = p.getSettingOrElse(Dimension.class, "main frame size", DEFAULT_FRAME_SIZE);
                Dimension currentSize = FRAME.getSize();
                if(!storedSize.equals(currentSize) && FRAME.getExtendedState() != JFrame.MAXIMIZED_BOTH) {
                    p.saveSetting("main frame size", currentSize);
                }
            }
        });
        getContext().stopModule(getContext());
        getContext().destroyModule(getContext());
    }
    
    public void startJvmKillerThread() {
        Thread killer = new Thread("JVM Killer!") {

            @Override
            public void run() {
                System.err.println("Starting killer thread...");
                try {
                    Thread.sleep(10000);
                    System.err.println("Jvm won't die, i'll kill ya! Arrrr...");
                    System.exit(0);
                } catch (Exception ex) {
                    System.err.println("Exception killing jvm " + ex);
                    return;
                }
            }
        };
        killer.setDaemon(true);
        killer.start();
    }
    
    /**
     * Add a component to the menu bar
     * @param c the component to add
     */
    public void installMenuComponent(Component c) {
        boolean isJMenu = c instanceof JMenu;
        if(isJMenu) {
            installJMenu((JMenu) c);
        } else {
            Todo.implementThis();
        }
    }

    /* add a jmenu to the main menu bar */
    private void installJMenu(JMenu jMenu) {
        menuBar.add(jMenu);
        Todo.implementThis("merge menu with the same label");
    }

    public void popupYesNoDialog(String message, Runnable onYes) {
        final int option = JOptionPane.showConfirmDialog(getModalDialogsParent(), message, Labels.getInstance().getLabelValue(LabelKey.CONFIRM), JOptionPane.YES_NO_OPTION);
        if(option == JOptionPane.YES_OPTION) {
            onYes.run();
        }
    }

    public Component getModalDialogsParent() {
        return FRAME;
    }

    public void popupMessageDialog(String message) {
        JOptionPaneExt.showMessageDialog(getModalDialogsParent(), message);
    }

    public void repackTab(Component panel) {
        REGIONS.adaptRegionToPreferredSize(panel);
    }
}
